// 存在用泛型稳定后，改用泛型实现key的替换，key 只需要 comparable(int, string...)
package sofile

import (
	"errors"
	"io/ioutil"
	"os"
	"reflect"
	"sync"
	"sync/atomic"

	"gitee.com/kimsoso/go-utils/utils"
	"github.com/vmihailenco/msgpack/v5"
)

type Blockfile struct {
	sync.RWMutex
	path    string // path to main storage
	bfi     *blockIndexFile
	blocks  interface{} // map of block
	fileBuf []byte

	datalen int32
}

// 创建文件数据块存储
func NewBlockfile(path string, initBlocks interface{}) (*Blockfile, error) {
	blocksValue := reflect.ValueOf(initBlocks)

	if blocksValue.Kind() != reflect.Map {
		panic("initBlocks must be a map")
	}

	bf := &Blockfile{
		path:    path,
		fileBuf: []byte{},
		blocks:  initBlocks,
		bfi:     newIndexFile(path + ".idx"),
		datalen: 0,
	}

	if len(blocksValue.MapKeys()) > 0 {
		if err := bf.setInitBlocks(); err != nil {
			return nil, err
		}
	} else {
		bf.load()
	}

	return bf, nil
}

// 批量加载数据
func (bf *Blockfile) BatchLoad(blocks interface{}) error {
	bf.blocks = blocks
	return bf.setInitBlocks()
}

// 保存初始化数据
func (bf *Blockfile) setInitBlocks() error {
	rblocks := reflect.ValueOf(bf.blocks)
	mr := rblocks.MapRange()

	bf.bfi.clearBlocks()

	for mr.Next() {
		k := mr.Key().String()
		v := mr.Value().Interface()
		m := map[string]interface{}{}
		m[k] = v
		buf, _ := msgpack.Marshal(m)
		// buf, _ := yaml.Marshal(m)
		bf.fileBuf = append(bf.fileBuf, buf...)
		bf.bfi.addData(k, int64(len(buf)))
	}
	if err := bf.bfi._save(); err != nil {
		return err
	}
	if err := bf.writeBufToFile(); err != nil {
		return err
	}
	atomic.StoreInt32(&bf.datalen, int32(rblocks.Len()))
	return nil
}

// 返回现存的数据块
func (bf *Blockfile) GetBlocks() interface{} {
	return bf.blocks
}

// 加载本地存储数据到内存
func (bf *Blockfile) load() error {
	buf, _ := ioutil.ReadFile(bf.path)
	if len(buf) > 0 {
		e := msgpack.Unmarshal(buf, bf.blocks)
		// e := yaml.Unmarshal(buf, bf.blocks)
		if e == nil {
			atomic.StoreInt32(&bf.datalen, int32(len(bf.bfi.blocks)))
		}
		return e
	}
	return nil
}

// 增加一个数据块
func (bf *Blockfile) Add(key string, block interface{}) error {
	buf, err := bf.addData(key, block)
	if err != nil {
		return err
	}

	if err := utils.AppendFile(bf.path, buf); err != nil {
		return err
	}

	if err := bf.bfi.add(key, int64(len(buf))); err != nil {
		return err
	}

	atomic.AddInt32(&bf.datalen, 1)
	return nil
}

func (bf *Blockfile) addData(key string, block interface{}) (dataBuf []byte, err error) {
	rblocks := reflect.ValueOf(bf.blocks)
	rvalue := rblocks.MapIndex(reflect.ValueOf(key))

	if rvalue.IsValid() {
		return nil, errors.New("key:" + key + " already exist")
	}

	bbf := map[string]*interface{}{}
	bbf[key] = &block

	dataBuf, _ = msgpack.Marshal(bbf)

	rblocks.SetMapIndex(reflect.ValueOf(key), reflect.ValueOf(block))
	return dataBuf, nil
}

// 删除一个数据块
func (bf *Blockfile) Delete(key string) error {
	rblocks := reflect.ValueOf(bf.blocks)
	rvalue := rblocks.MapIndex(reflect.ValueOf(key))
	if !rvalue.IsValid() {
		return errors.New("key:" + key + " is not exist")
	}

	bfi := bf.bfi.getBlock(key)

	if err := utils.FileCropBlock(bf.path, bfi.Offset, bfi.Size); err == nil {
		if err := bf.bfi.delete(key); err != nil {
			return err
		}

		rk := reflect.ValueOf(key)
		rblocks.SetMapIndex(rk, reflect.Value{})
		atomic.AddInt32(&bf.datalen, -1)
		return nil
	} else {
		return err
	}
}

// 得到一个数据块
func (bf *Blockfile) Get(key string) (out interface{}, exist bool) {
	rblocks := reflect.ValueOf(bf.blocks)
	rvalue := rblocks.MapIndex(reflect.ValueOf(key))
	exist = rvalue.IsValid()
	if exist {
		out = rvalue.Interface()
	} else {
		out = nil
	}
	return
}

// 更新一个数据块
func (bf *Blockfile) Update(key string, block interface{}) error {
	rblocks := reflect.ValueOf(bf.blocks)
	rvalue := rblocks.MapIndex(reflect.ValueOf(key))

	if !rvalue.IsValid() {
		return errors.New("key:" + key + " is not exist")
	}

	blocks := map[string]*interface{}{}
	blocks[key] = &block

	blockBuf, err := msgpack.Marshal(blocks)
	// blockBuf, err := yaml.Marshal(blocks)
	if err != nil {
		return err
	}

	bfi := bf.bfi.getBlock(key)

	if bfi.Size == int64(len(blockBuf)) {
		return utils.FileReplaceBlock(bf.path, bfi.Offset, bfi.Size, blockBuf)
	} else {
		leftBuf, err := utils.FileReadToEnd(bf.path, bfi.Offset+bfi.Size)
		if err != nil {
			return err
		}

		os.Truncate(bf.path, bfi.Offset)

		err = utils.AppendFile(bf.path, blockBuf)
		if err != nil {
			return err
		}

		err = utils.AppendFile(bf.path, leftBuf)
		if err != nil {
			return err
		}

		err = bf.bfi.set(key, int64(len(blockBuf)))
		if err != nil {
			return err
		}
	}
	rblocks.SetMapIndex(reflect.ValueOf(key), reflect.ValueOf(block))
	return nil
}

func (bf *Blockfile) writeBufToFile() error {
	return utils.WriteFile(bf.path, bf.fileBuf, os.ModePerm)
}

// 数据长度
func (bf *Blockfile) GetLen() int {
	datalen := int(atomic.LoadInt32(&bf.datalen))
	if datalen <= 0 {
		return 0
	} else {
		return datalen
	}
	// value := reflect.ValueOf(bf.blocks)
	// return len(value.MapKeys())
}

// 摧毁整个结构
func (bf *Blockfile) Destroy() {
	bf.blocks = map[string]interface{}{}
	os.Remove(bf.path)
	atomic.StoreInt32(&bf.datalen, 0)
	bf.bfi.destroy()
}

// 清除数据
func (bf *Blockfile) Clean() {
	os.Truncate(bf.path, 0)

	val := reflect.ValueOf(bf.blocks)
	iter := val.MapRange()
	for iter.Next() {
		val.SetMapIndex(iter.Key(), reflect.Value{})
	}

	atomic.StoreInt32(&bf.datalen, 0)
	bf.bfi.clean()
}
